---
title: Artifacts
description: Interactive code and document editing with version management
---

Artifacts are interactive, editable content that appears in the chat canvas. They allow users to view, edit, and interact with generated code and documents directly within the chat interface, similar to OpenAI's ChatGPT canvas feature.

## Artifact System Overview

The artifact system consists of:

- **ChatCanvas** - Side panel for displaying artifacts
- **Artifact Viewers** - Components for rendering different artifact types
- **Version Management** - Track changes and revisions
- **Edit Capabilities** - In-place editing with syntax highlighting

### Artifact Types

The system supports two main artifact types:

1. **Code Artifacts** - Interactive code editing with syntax highlighting
2. **Document Artifacts** - Rich text document editing with markdown support

## Code Artifacts

Code artifacts provide interactive code editing with full syntax highlighting and language support.

### Creating Code Artifacts

```typescript
// Server-side: Create code artifact part
const codeArtifact = {
  type: 'data-artifact',
  data: {
    type: 'code',
    data: {
      title: 'Data Visualization Script',
      file_name: 'visualize_data.py',
      language: 'python',
      code: `
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

def create_sales_chart(data_file):
    """Create a sales visualization chart."""
    # Load the data
    df = pd.read_csv(data_file)

    # Prepare the data
    monthly_sales = df.groupby('month')['revenue'].sum()

    # Create the visualization
    plt.figure(figsize=(12, 6))
    bars = plt.bar(monthly_sales.index, monthly_sales.values,
                   color='steelblue', alpha=0.8)

    # Customize the chart
    plt.title('Monthly Sales Revenue', fontsize=16, fontweight='bold')
    plt.xlabel('Month', fontsize=12)
    plt.ylabel('Revenue ($)', fontsize=12)
    plt.xticks(rotation=45)

    # Add value labels on bars
    for bar in bars:
        height = bar.get_height()
        plt.text(bar.get_x() + bar.get_width()/2., height,
                f'${height:,.0f}', ha='center', va='bottom')

    plt.tight_layout()
    plt.grid(True, alpha=0.3)
    plt.show()

    return monthly_sales

# Example usage
if __name__ == "__main__":
    sales_data = create_sales_chart('sales_data.csv')
    print(f"Total annual revenue: ${sales_data.sum():,.2f}")
      `
    }
  }
}
```

### Streaming Code Artifacts

```typescript
// In your API route
export async function POST(request: Request) {
  const { messages } = await request.json()

  const stream = new ReadableStream({
    async start(controller) {
      const encoder = new TextEncoder()

      // Send initial response
      controller.enqueue(
        encoder.encode(
          '0:"I\'ll create a Python script for data visualization:\\n"\\n'
        )
      )

      // Generate code (could be from LLM)
      const generatedCode = await generateCodeWithLLM(messages)

      // Send code artifact
      const artifact = {
        type: 'data-artifact',
        data: {
          type: 'code',
          data: {
            title: 'Data Visualization Script',
            file_name: 'chart_generator.py',
            language: 'python',
            code: generatedCode,
          },
        },
      }

      // send the artifact with the data: prefix for SSE format
      controller.enqueue(encoder.encode(`data: ${JSON.stringify(artifact)}\\n`))

      // Send follow-up text
      controller.enqueue(
        encoder.encode(
          '0:"You can edit this code directly in the canvas. Would you like me to explain any part of it?"\\n'
        )
      )

      controller.close()
    },
  })

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Connection': 'keep-alive',
    },
  })
}
```

### Supported Languages

The code artifact system supports syntax highlighting for:

- **JavaScript/TypeScript** - `.js`, `.ts`, `.jsx`, `.tsx`
- **Python** - `.py`, `.pyx`
- **CSS/SCSS** - `.css`, `.scss`, `.sass`
- **HTML** - `.html`, `.htm`
- **JSON** - `.json`
- **Markdown** - `.md`, `.mdx`
- **SQL** - `.sql`
- **Shell/Bash** - `.sh`, `.bash`

### Code Artifact Features

```tsx
// The canvas automatically provides these features:
// - Syntax highlighting
// - Line numbers
// - Code folding
// - Search and replace
// - Auto-indentation
// - Bracket matching
// - Error highlighting (for supported languages)
```

## Document Artifacts

Document artifacts provide rich text editing with markdown support and real-time preview.

### Creating Document Artifacts

```typescript
const documentArtifact = {
  type: 'data-artifact',
  data: {
    type: 'document',
    data: {
      title: 'API Integration Guide',
      content: `
# API Integration Guide

## Overview

This guide walks you through integrating our REST API into your application.

## Authentication

All API requests require authentication using API keys:

\`\`\`bash
curl -H "Authorization: Bearer YOUR_API_KEY" \\
     https://api.example.com/v1/users
\`\`\`

## Endpoints

### User Management

#### Get User Profile

\`\`\`
GET /v1/users/{userId}
\`\`\`

**Response:**
\`\`\`json
{
  "id": "user_123",
  "name": "John Doe",
  "email": "john@example.com",
  "created_at": "2024-01-15T10:30:00Z"
}
\`\`\`

#### Update User Profile

\`\`\`
PUT /v1/users/{userId}
Content-Type: application/json

{
  "name": "John Smith",
  "email": "john.smith@example.com"
}
\`\`\`

### Data Operations

#### List Records

\`\`\`
GET /v1/records?limit=20&offset=0
\`\`\`

**Query Parameters:**
- \`limit\`: Number of records to return (default: 20, max: 100)
- \`offset\`: Number of records to skip (default: 0)
- \`sort\`: Sort field (default: created_at)
- \`order\`: Sort order (asc/desc, default: desc)

## Error Handling

The API uses standard HTTP status codes:

| Code | Description |
|------|-------------|
| 200  | Success |
| 400  | Bad Request |
| 401  | Unauthorized |
| 404  | Not Found |
| 500  | Internal Server Error |

**Error Response Format:**
\`\`\`json
{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "The request is missing required parameters",
    "details": {
      "missing_fields": ["name", "email"]
    }
  }
}
\`\`\`

## Rate Limiting

- **Free Tier**: 100 requests per hour
- **Pro Tier**: 1,000 requests per hour
- **Enterprise**: Custom limits

Rate limit headers are included in all responses:

\`\`\`
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200
\`\`\`

## SDKs and Libraries

We provide official SDKs for popular languages:

### JavaScript/Node.js

\`\`\`bash
npm install @example/api-client
\`\`\`

\`\`\`javascript
import { ApiClient } from '@example/api-client'

const client = new ApiClient({
  apiKey: 'your-api-key'
})

const user = await client.users.get('user_123')
console.log(user)
\`\`\`

### Python

\`\`\`bash
pip install example-api-client
\`\`\`

\`\`\`python
from example_api import ApiClient

client = ApiClient(api_key='your-api-key')
user = client.users.get('user_123')
print(user)
\`\`\`

## Best Practices

1. **Always use HTTPS** for API requests
2. **Store API keys securely** - never commit them to version control
3. **Implement retry logic** with exponential backoff
4. **Cache responses** when appropriate to reduce API calls
5. **Monitor rate limits** and implement proper handling

## Webhooks

Configure webhooks to receive real-time notifications:

\`\`\`json
{
  "url": "https://yourapp.com/webhooks/api",
  "events": ["user.created", "user.updated", "user.deleted"],
  "secret": "webhook-secret-key"
}
\`\`\`

## Support

- **Documentation**: https://docs.example.com
- **Support Email**: support@example.com
- **Status Page**: https://status.example.com

Happy coding! 🚀
      `,
    },
  },
}
```

### Document Features

Document artifacts provide:

- **Rich Text Editing** - WYSIWYG editor with markdown output
- **Live Preview** - Real-time rendering of markdown content
- **Formatting Tools** - Bold, italic, headers, lists, links
- **Code Blocks** - Syntax-highlighted code snippets
- **Tables** - Table creation and editing
- **Math Support** - LaTeX math rendering
- **Image Embedding** - Image insertion and management

## Canvas Integration

### Basic Canvas Setup

```tsx
import { ChatSection, ChatCanvas } from '@llamaindex/chat-ui'

function ChatWithCanvas() {
  const handler = useChat({ 
    transport: new DefaultChatTransport({
      api: '/api/chat',
    }),
  })

  return (
    <ChatSection handler={handler} className="flex h-full">
      <div className="flex-1">
        <ChatMessages />
        <ChatInput />
      </div>
      <ChatCanvas className="w-1/2 border-l" />
    </ChatSection>
  )
}
```

### Adding Custom Artifact Viewers

You can extend the canvas with custom artifact viewers to handle different types of content. Here's an example of creating a custom image artifact viewer:

```tsx
import { 
  Artifact, 
  ChatCanvas, 
  useChatCanvas 
} from '@llamaindex/chat-ui'
import { Image } from 'lucide-react'

// Define your custom artifact type
type ImageArtifact = Artifact<
  {
    imageUrl: string
    caption: string
  },
  'image'
>

function ImageArtifactViewer() {
  const { displayedArtifact } = useChatCanvas()

  // Only render for image artifacts
  if (displayedArtifact?.type !== 'image') return null

  const {
    data: { imageUrl, caption },
  } = displayedArtifact as ImageArtifact

  return (
    <div className="flex min-h-0 flex-1 flex-col">
      {/* Custom header with icon and title */}
      <div className="flex items-center justify-between border-b p-4">
        <h3 className="flex items-center gap-3 text-gray-600">
          <Image className="size-8 text-blue-500" />
          <div className="font-semibold">{caption}</div>
        </h3>
        
        {/* Use built-in canvas actions */}
        <ChatCanvas.Actions>
          <ChatCanvas.Actions.History />
          <ChatCanvas.Actions.Close />
        </ChatCanvas.Actions>
      </div>
      
      {/* Custom content area */}
      <div className="flex flex-1 items-center justify-center p-4">
        <img
          src={imageUrl}
          alt={caption}
          className="h-[70%] rounded-2xl shadow-2xl"
        />
      </div>
    </div>
  )
}

// Use the custom viewer in your chat
function CustomChat() {
  const handler = useChat({ initialMessages: [] })

  return (
    <ChatSection handler={handler}>
      <div className="flex h-full flex-col">
        <ChatMessages />
        <ChatInput />
      </div>
      
      <ChatCanvas>
        {/* Built-in artifact viewers */}
        <ChatCanvas.CodeArtifact />
        <ChatCanvas.DocumentArtifact />
        
        {/* Your custom artifact viewer */}
        <ImageArtifactViewer />
      </ChatCanvas>
    </ChatSection>
  )
}
```

You can also customize ArtifactCard for your artifact type:

```tsx
import { Image } from 'lucide-react'

// custom artifact card for image artifacts with title is the caption and icon is the image icon
function CustomArtifactCard({ data }: { data: Artifact }) {
  return (
    <ChatCanvas.Artifact
      data={data}
      getTitle={artifact => (artifact as ImageArtifact).data.caption}
      iconMap={{ image: Image }}
    />
  )
}
```

To trigger your custom artifact viewer, the AI response should include a part with the matching artifact type:

```ts
message = {
  parts: [
    {
      type: 'data-artifact',
      data: {
        type: 'code',
        data: {
          title: 'Data Visualization Script',
          file_name: 'visualize_data.py',
          language: 'python',
          code: 'import matplotlib.pyplot as plt\n# Code...',
        },
      }
    }
  ]
} 
```

You can create multiple custom artifact viewers for different content types:

```tsx
<ChatCanvas>
  {/* Built-in viewers */}
  <ChatCanvas.CodeArtifact />
  <ChatCanvas.DocumentArtifact />
  
  {/* Custom viewers */}
  <ImageArtifactViewer /> {/* for image artifacts */}
  <PDFArtifactViewer /> {/* for pdf artifacts */}
  <ChartArtifactViewer /> {/* for chart artifacts */}
</ChatCanvas>
```

For a complete working example of custom artifact viewers, check out the demo implementation in [`apps/web/app/demo/canvas/custom/page.tsx`](https://github.com/run-llama/chat-ui/blob/main/apps/web/app/demo/canvas/custom/page.tsx). This demo shows:

- Custom `ImageArtifactViewer` implementation
- Integration with existing chat components
- Sample messages with artifact parts
- Copy-to-clipboard functionality for the code

### Canvas Auto-Show

The canvas automatically appears when artifacts are present:

```tsx
// Canvas appears automatically when message contains artifacts
<ChatMessage message={messageWithArtifact}>
  <ChatMessage.Content>
    <ChatMessage.Part.Artifact />
  </ChatMessage.Content>
</ChatMessage>
```

## Version Management

Artifacts support version tracking and restoration:

### Version History

```tsx
import { useChatCanvas } from '@llamaindex/chat-ui'

function ArtifactVersions() {
  const { currentArtifact, updateArtifact } = useChatCanvas()

  if (!currentArtifact) return null

  const versions = currentArtifact.versions || []

  const restoreVersion = (version: any) => {
    updateArtifact({
      ...currentArtifact,
      content: version.content,
      versions: [
        ...versions,
        {
          id: generateId(),
          content: currentArtifact.content,
          timestamp: new Date().toISOString(),
          description: 'Current version backup',
        },
      ],
    })
  }

  return (
    <div className="space-y-2">
      <h3 className="font-semibold">Version History</h3>
      {versions.map((version, index) => (
        <div key={version.id} className="flex items-center justify-between">
          <div>
            <p className="text-sm font-medium">
              Version {versions.length - index}
            </p>
            <p className="text-xs text-gray-500">
              {new Date(version.timestamp).toLocaleString()}
            </p>
          </div>
          <button
            onClick={() => restoreVersion(version)}
            className="text-sm text-blue-600 hover:underline"
          >
            Restore
          </button>
        </div>
      ))}
    </div>
  )
}
```

### Save Changes

```tsx
function SaveArtifactChanges() {
  const { currentArtifact, updateArtifact } = useChatCanvas()
  const [hasChanges, setHasChanges] = useState(false)

  const saveChanges = () => {
    if (!currentArtifact || !hasChanges) return

    // Create version backup
    const newVersion = {
      id: generateId(),
      content: currentArtifact.originalContent,
      timestamp: new Date().toISOString(),
      description: 'Auto-saved version',
    }

    updateArtifact({
      ...currentArtifact,
      versions: [...(currentArtifact.versions || []), newVersion],
      originalContent: currentArtifact.content,
    })

    setHasChanges(false)
  }

  return (
    <button
      onClick={saveChanges}
      disabled={!hasChanges}
      className="rounded bg-blue-600 px-3 py-1 text-white disabled:opacity-50"
    >
      {hasChanges ? 'Save Changes' : 'Saved'}
    </button>
  )
}
```

## Artifact Actions

### Export Functionality

```tsx
function ArtifactExport() {
  const { currentArtifact } = useChatCanvas()

  const exportAsFile = () => {
    if (!currentArtifact) return

    const content =
      currentArtifact.type === 'code'
        ? currentArtifact.code
        : currentArtifact.content

    const filename =
      currentArtifact.type === 'code'
        ? currentArtifact.file_name
        : `${currentArtifact.title}.md`

    const blob = new Blob([content], {
      type: 'text/plain;charset=utf-8',
    })

    const url = URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = url
    link.download = filename
    link.click()

    URL.revokeObjectURL(url)
  }

  return (
    <button
      onClick={exportAsFile}
      className="flex items-center gap-2 rounded border px-3 py-1 text-sm"
    >
      <DownloadIcon className="h-4 w-4" />
      Export
    </button>
  )
}
```

### Copy to Clipboard

```tsx
import { useCopyToClipboard } from '@llamaindex/chat-ui'

function CopyArtifact() {
  const { currentArtifact } = useChatCanvas()
  const { copyToClipboard, isCopied } = useCopyToClipboard()

  const handleCopy = () => {
    if (!currentArtifact) return

    const content =
      currentArtifact.type === 'code'
        ? currentArtifact.code
        : currentArtifact.content

    copyToClipboard(content)
  }

  return (
    <button
      onClick={handleCopy}
      className="flex items-center gap-2 rounded border px-3 py-1 text-sm"
    >
      <CopyIcon className="h-4 w-4" />
      {isCopied ? 'Copied!' : 'Copy'}
    </button>
  )
}
```

## Next Steps

- [Examples](./examples.mdx) - See complete artifact implementations
- [Customization](./customization.mdx) - Style and customize artifact appearance
- [Widgets](./widgets.mdx) - Explore related widget functionality
- [Parts](./parts.mdx) - Understand the message parts system
